Using Cobra for Advanced CLI Applications

Learn how to use Cobra for advanced CLI applications in Go.

Cobra is a set of packages that allows a developer to create more complex CLI applications. This becomes more useful than just the standard flag package when the complexity of an application causes a list of flags to become numerous.

In this lesson, we’ll talk about how to use Cobra to create structured CLI applications that are friendly to developers to add features and allow users to understand what is available in an application.

svg viewer

A few features that Cobra provides are as follows:

  • Nested subcommands

  • Command suggestions

  • Aliases for commands so that we can make changes without breaking users

  • Generation of help text from flags and commands

  • Generation of auto-completion for various shells

  • Man page creation

This section will borrow heavily from the Cobra documentation, which you can find here.

Code organization#

To make effective use of Cobra and make it easy for developers to understand where to add and change commands, Cobra suggests the following structure:

Cobra directory structure

This structure has our main main.go executable at the top-level directory and all of our commands under cmd/.

The main file for a Cobra application is primarily used to simply initialize Cobra and let it perform command executions. The file will look like this:

The main.go file

Next, we'll look at using the Cobra generator application to generate boilerplate code.

The optional Cobra generator#

Cobra provides an application that can generate boilerplate code for our application. To get started with the generator, the configuration file for our application in our root directory called ~/.cobra.yaml contains the following:

A basic configuration file

This will handle printing our MIT license. We can use any of these values for the following built-in licenses:

  • GPLv2

  • GPLv3

  • LGPL

  • AGPL

  • 2-Clause BSD

  • 3-Clause BSD

By default, Cobra will use this configuration file from our home directory. If we need a different license, put the configuration in our repository and use cobra --config="config/location.yaml to use the alternate configuration file.

To download Cobra and build with the Cobra generator, the following commands are used:

Terminal commands to install Cobra

The application can be initialized in the new application's root directory:

Terminal command to initialize the application

Note: [repo path] will be something such as github.com/spf13/newApp.

Commands can be created for the application with the following:

Commands to create files

This delivers the following:

The generated file directory

Note: We are required to use camelCase for command names. Not doing this will cause us to encounter errors.

The -p option for create is used to make it a subcommand of config. The string that follows is the parent's name plus Cmd. All other add calls have -p set to rootCmd. The application can be run with the following commands after being built:

Commands to run the application

With the boilerplate now in place, we'll only need to configure the commands to execute.

The command package#

In the cmd package that has been generated, we'll find a file for each command that can be executed. We'll need to modify each file to give the correct help text, use flags, and execute the command. We'll look at a generated cmd/get.go file for an application created with the following commands:

Commands to generate get.go

This application will talk to the QOTD server that we created earlier in the course. The generated cmd/get.go file will look similar to this:

The cmd/get.go file that defines a command

This code does the following:

  • Line 1: Creates a variable whose name is based on the command name plus Cmd.

  • Line 2: Use is the argument name for the command line.

  • Line 3: Short is the brief description.

  • Line 4: Long is a longer description with examples.

  • Line 5: Run is the entry point for the code we want to execute.

  • Lines 10–12: Defines init(), which adds the command to the rootCmd object.

This is used to write the QOTD CLI:

Writing to our QOTD CLI

This code does the following:

  • Lines 2–10: Sets up an addr variable to hold our server address:

    • If --dev is passed, it sets addr to devAddr

    • Otherwise, it uses the --addr flag's value.

    • --addr defaults to 127.0.0.1:80.

  • Line 12: Creates a new client for our QOTD server

  • Line 18: Calls the QOTD server:

    • Uses Context passed to *cobra.Command.

    • Uses the --author flag value, which defaults to an empty string.

  • Lines 24–39: Uses a --json flag to determine whether the output should be in JSON:

    • If JSON, it outputs an inline-defined struct as JSON.

    • Otherwise, it just prints it to the screen.

Note: We'll see the mustBool() and mustString() functions. These simply return the value from the flag name that is passed. If the flag isn't defined, it panics. This removes a lot of ugly code for something that must always work for the CLI application to be valid. These functions are in the repository version.

The flags that you see are not from the standard library flag package. Instead, this package uses flag types. This package has more built-in types and methods than the standard flag package.

The flags can be defined by using the Run function:

Defining the flags in our Run function

This code does the following:

  • Line 3: Adds a flag called --dev that can be shortened to -d and defaults to false.

  • Line 4: Adds a flag called --addr that defaults to "127.0.0.1:80".

  • Line 5: Adds a flag called --author that can be shortened to -a.

  • Line 6: Adds a flag called --json that defaults to false.

Note: Methods followed by P, such as BoolP(), define shortened flags as well as the long flag names.

The flags we defined are only available when the get command is invoked. If we create subcommands on get, these will only be available on get with no sub-commands defined.

To add flags that work on all subcommands, use .PersistentFlags() instead of .Flags().

Now, we can run our app and call this command. In the code widget below, the following commands are run:

Commands to run the application

This runs our application using the server at the 127.0.0.1:3560 address and requests a quote from Eleanor Roosevelt, with output in JSON format:

/
start.sh
cmd
get.go
root.go
main.go
Executable application that runs the above commands

The following command gets a random quote from the server at the address 127.0.0.1:3560

Get random quote from the server

In this lesson, we have learned what the Cobra package is, how to use the Cobra generator tool to bootstrap a CLI application, and finally, how to build commands for our application using this package.

Accessing Non-Flag Arguments

Handling OS Signals